<?php
/**
 * Created by PhpStorm.
 * User: s.utkin
 * Date: 16.04.2015
 * Time: 18:22
 */

namespace Jobs;


use LogCollector\Handlers\LogHandlerInterface;

class LogCollectorAction extends \Action
{
	/**
	 * @var LogHandlerInterface[]
	 */
	protected $handlers = [];

	protected function run()
	{
		$this->response->noCache(true);
		$this->response->noRender(true);

		# создаем список хэндлеров
		$handlersConfig = \LogCollectorConfig::get('handlers');
		foreach ($handlersConfig as $handlerConfig) {
			$className = $handlerConfig['class'];
			$classParams = isset($handlerConfig['params']) ? $handlerConfig['params'] : [];
			$handlerInstance = new $className($classParams);
			if (!($handlerInstance instanceof LogHandlerInterface)) {
				continue;
			}
			$this->handlers[] = $handlerInstance;
		}

		# получаем путь к корневой папке логов
		$defaultLogFile = \Application::logFile();
		$logDir = dirname($defaultLogFile);

		# если корневой папки еще нет, то доступные логи не появятся, как минимум минуту
		if (!file_exists($logDir)) {
			echo "Log dir doesn't exists: '$logDir'";
			return;
		}

		# получаем спиок файлов в корневой папке
		$objects = $this->getFiles($logDir);

		# перебираем все файлы
		$i = 0;
		foreach ($objects as $name => $object) {
			# если не файл - пропускаем
			if (!is_file($name)) {
				continue;
			}

			# если файл последний раз модифицировался менее чем 90 секунд назад - пропускаем его
			$modifyTime = filemtime($name);
			if ((time() - $modifyTime) < 90) {
				continue;
			}

			$i++;
			echo " -$i- \n";
			echo $name . "\n";

			# запускам onBeforeParse хэндлеров
			array_walk(
				$this->handlers,
				function (LogHandlerInterface $handler) {
					$handler->onBeforeParse(null);
				}
			);

			# парсим файл
			$this->parseFile($name);

			# запускам onAfterParse хэндлеров
			array_walk(
				$this->handlers,
				function (LogHandlerInterface $handler) {
					$handler->onAfterParse(null);
				}
			);

			unlink($name);
		}

		echo "files per iteration $i\n"; ob_flush();
	}

	/**
	 * Возвращает итератор по каталогам и файлам корневого каталога
	 *
	 * @param $logDir - путь к корневому каталогу
	 * @return \RecursiveIteratorIterator
	 */
	protected static function getFiles($logDir)
	{
		return new \RecursiveIteratorIterator(
			new \RecursiveDirectoryIterator($logDir),
			\RecursiveIteratorIterator::SELF_FIRST
		);
	}

	/**
	 * Парсит лог-файл
	 *
	 * @param $fileName
	 * @throws \Exception
	 */
	protected function parseFile($fileName)
	{
		# вычисляем имя экшена
		$parts = explode(DIRECTORY_SEPARATOR, $fileName);
		$count = count($parts);
		$actionName = $parts[$count - 2];

		# получаем итератор строк лог-файла
		$fileLines = $this->getFileLines($fileName);
		$item = null;

		# обрабатываем каждую строку лог-файла
		$time = microtime(true);
		$cnt = 0;
		$itms = 0;
		$lng = 0;
		foreach ($fileLines as $line) {
			$cnt++;
			$lng += strlen($line);

			# встретили маркер новой закписи
			if (substr($line, 0, 9) === '~PNT-LOG~') {
				# запись лог-файла полностью сформирована
				if ($item !== null) {
					$itms++;
					# передаем запись на обработку хэндлерам
					array_walk(
						$this->handlers,
						function (LogHandlerInterface $handler) use ($item) {
							$handler->process($item);
						}
					);
				}

				# начинем формировать новую запись
				$parts = explode(' ', $line);
				array_shift($parts); // маркер
				$item = [
					'action' => trim($actionName),
					'type' => trim(array_shift($parts)),
					'time' => trim(array_shift($parts)),
					'title' => trim(implode(' ', $parts)),
					'data' => ''
				];

				continue;
			}

			# если в начале файла есть строки без маркера лог-записи - пропускаем эти строки
			if ($item === null) {
				continue;
			}

			# собираем данные лог-записи
			$item['data'] .= $line;
		}

		# запись лог-файла полностью сформирована
		if ($item !== null) {
			$itms++;
			# передаем запись на обработку хэндлерам
			array_walk(
				$this->handlers,
				function (LogHandlerInterface $handler) use ($item) {
					$handler->process($item);
				}
			);
		}

		echo microtime(true) - $time, " items: $itms, lines: $cnt, bytes: $lng\n";ob_flush();
	}

	/**
	 * Возвращает генератор, итерирующий указанный файл по строкам
	 *
	 * @param $fileName
	 * @return \Generator
	 * @throws \Exception
	 */
	protected function getFileLines($fileName)
	{
		$fileHandler = fopen($fileName, 'r');

		if (!$fileHandler) {
			throw new \Exception("File '$fileName' not found.");
		}

		while (false !== $row = fgets($fileHandler, 8192)) {
			yield $row;
		}

		fclose($fileHandler);
	}
} 